﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TwlNS
{
    public abstract class Node
    {
        public static int InitialEnergy { get; set; }
        public static int SensingEnergyCost { get; set; }
        public static int ProcessingEnergyCost { get; set; }
        public static int SendingEnergyCost { get; set; }
        public static int ReceivingEnergyCost { get; set; }

		public static int MaxChildren { get; set; }
		public static int HopCountThreshold { get; set; }
		public static bool ReverseHopCountThreshold { get; set; }
		protected bool _unlimitedChildren;
		private static int IdCounter = 0;
		private static int GetNextId() {
			return IdCounter++;
		}

        protected int _Energy;
        public int Energy
        {
            get
            {
                return _Energy;
            }
        }
        public bool ReceivedMsg { get; set; }
        
    	protected int _Id;
		public int Id { get { return _Id; } }

		//Indicate what ever can connect to sink via links
		protected bool _Sinked;
		public bool Sinked { get { return _Sinked; } }

		protected int _HopCount;
		public int HopCount { get { return _HopCount; } }

		private List<Node> _RadioRangeNeighbor;
		public List<Node> RadioRangeNeighbor {
			get { return _RadioRangeNeighbor; }
		}

		private List<Node> _PossibleParents;
		public List<Node> PossibleParents {
			get { return _PossibleParents; }
		}
		private List<Node> _PossibleChildren;
		public List<Node> PossibleChildren {
			get { return _PossibleChildren; }
		}

    	protected List<Node> _ParentNodes;
		public List<Node> ParentNodes { get { return _ParentNodes; } }

		private List<Node> _Children;
		public List<Node> Children { get { return _Children; } }

		private class x : IComparer<Node> {
			public int Compare(Node x, Node y) {
				return x.Id - y.Id;
			}
		}

		private List<int> _SetNumbers;
		public List<int> SetNumbers { get {return _SetNumbers; } }
		public void SortRadioRangeNeighbor() {
			_RadioRangeNeighbor.Sort(new x());
		}

		public bool Activated { get; set; }

		public bool AddSetNumber(int setNumber){
			if (!_SetNumbers.Contains(setNumber)){
				_SetNumbers.Add(setNumber);
				return true;
			}
			return false;
		}
		public bool AddSetNumber(List<int> newSetNumbers){
			bool alreadyAdded = true;
			foreach (var number in newSetNumbers){
				if (!_SetNumbers.Contains(number)){
					_SetNumbers.Add(number);
					alreadyAdded = false;
				}
			}
			return !alreadyAdded;
		}
		public bool AddRadioRangeNeighbor(Node node) {
			Coord n0 = Location;
			Coord n1 = node.Location;

			if (node != this
				&& Math.Abs(n0.X - n1.X) <= Environment.RadioRadius
				&& Math.Abs(n0.Y - n1.Y) <= Environment.RadioRadius
				//&& !RadioRangeNeighbor.Contains(node)
				&& Location.distance(node.Location) <= Environment.RadioRadius) {

				_RadioRangeNeighbor.Add(node);
				node._RadioRangeNeighbor.Add(this);
				return true;
			}
			return false;
		}

		public bool AddPossibleParent(Node node) {
			if (node.HopCount >= HopCount)
				return false;
			_HopCount = node.HopCount + 1;
			//Remove higher hop count possible parent
			/*
			for (int i = PossibleParents.Count - 1; i >= 0;i-- ) {
				if (PossibleParents[i].HopCount > node.HopCount) {
					PossibleParents[i].PossibleChildren.Remove(this);
					_PossibleParents.RemoveAt(i);
				}	
			}
			//*/
			_PossibleParents.Add(node);
			node._PossibleChildren.Add(node);
			return true;
		}

		public void CleanPossibleParent(){
			_PossibleParents.Clear();
			foreach (var neighbor in RadioRangeNeighbor){
				if(neighbor.HopCount<=HopCount)
					_PossibleParents.Add(neighbor);
			}
			/*
			for (int i = PossibleParents.Count - 1; i >= 0; i--) {
				if (PossibleParents[i].HopCount >= _HopCount) {
					PossibleParents[i].PossibleChildren.Remove(this);
					_PossibleParents.RemoveAt(i);
				}
			}*/
		}
		public void FindAndSetParent(){
			if (PossibleParents.Count == 0)
				return;
			if (Environment.OriginalImplementation){
				var parents = new List<Node>();
				foreach (var parent in _PossibleParents){
					if(parent.HopCount<HopCount)
						parents.Add(parent);
				}
				if(parents.Count==0)
					return;
				_ParentNodes.Add( parents[new Random().Next(parents.Count)]);
				if (parents.Count > 1 && Environment.N2){
					parents.Remove(_ParentNodes[0]);
					_ParentNodes.Add(parents[new Random().Next(parents.Count)]);
				}

			}else{
				double MaxWeight1 = double.MinValue;
				double MaxWeight2 = double.MinValue;
				int MaxWeightIdx1=-1, MaxWeightIdx2 = -1;
				for (int i = 0; i < _PossibleParents.Count; i++){
					
					var parent = PossibleParents[i];
					//Prevent forming loops
					if(Children.Contains(parent))
						continue;
					double weight = Environment.HopCountWeight*(HopCount - parent.HopCount)/HopCount +
					                Environment.EnergyWeight*parent.Energy/InitialEnergy;
					if (parent is SinkNode)
						weight = double.MaxValue;
					if (weight > MaxWeight1){
						MaxWeight2 = MaxWeight1;
						MaxWeight1 = weight;
						MaxWeightIdx2 = MaxWeightIdx1;
						MaxWeightIdx1 = i;
					} else if (weight > MaxWeight2){
						MaxWeightIdx2 = i;
						MaxWeight2 = weight;
					}
				}
				if(MaxWeightIdx1>=0)
					_ParentNodes.Add(_PossibleParents[MaxWeightIdx1]);
				if(MaxWeightIdx2!=MaxWeightIdx1 && MaxWeightIdx2>=0 && Environment.N2)
					_ParentNodes.Add(_PossibleParents[MaxWeightIdx2]);
				
			}
			foreach (var parentNode in ParentNodes){
				parentNode.Children.Add(this);
			}
		}
		/*
		public bool AddChildren(Node node) {
			if (Children.Count == MaxChildren && !_unlimitedChildren)
				return false;
			if (((Children.Count >= 1 && HopCount >= HopCountThreshold && !ReverseHopCountThreshold) ||
				(Children.Count >= 1 && HopCount < HopCountThreshold && ReverseHopCountThreshold))
				&& !_unlimitedChildren)
				return false;
			Children.Add(node);
			node._ParentNodes = this;
			return true;
		}*/
		public void SetSinkedRecursive() {
			if(_Sinked)
				return;
			_Sinked = true;
			foreach (var child in Children) {
				child.SetSinkedRecursive();
			}
		}
		public void ActivateSetNumberRecursive(int setNumber) {
			if(_CurrentSetNumber==setNumber)
				return;
			_CurrentSetNumber = setNumber;
			if (SetNumbers.Contains(setNumber)){
				if(!Activated)
					Activated = true;
			} else if(Activated){
				Activated = false;
			}
			foreach (var child in Children) {
				child.ActivateSetNumberRecursive(setNumber);
			}
		}

    	private int _CurrentSetNumber;
		protected Node() {
			_Id = GetNextId();
			_RadioRangeNeighbor = new List<Node>();
			_PossibleParents = new List<Node>();
			_PossibleChildren = new List<Node>();
			_Children = new List<Node>();
			_SetNumbers = new List<int>();
			_ParentNodes = new List<Node>();
			_unlimitedChildren = false;
			_Sinked = false;
			_CurrentSetNumber = -1;
		}

		public Coord Location { get; set; }
		public abstract void Initialization();
		public abstract void Pulse();
	}
}
